#include "Network.h"
//warning, one should never have two networks running on the same machine
Network::Network():server(5555)//default constructor
{
	
    //robotaddress = "/home/pi/csr-repository/CommunicationLib/swarmlist.txt";//default list of robots
}
/*Network::Network(string fileName):server(5555)//file name input constructor sets the address of the file name
{
	robotaddress = fileName;
}*/
Network::Network(vector<string> ipList):server(5555)//give a vector of IPs
{
    //robotaddress = "";
    ips[0] = "not applicable";
    for(int i = 0; i < ipList.size(); i++)
	{
        ips[i+1] = ipList[i];
	}
}
void Network::init()
{	
	int b;
    //if(robotaddress != "")//if we have a robot address use the robot function
    //{
    //	robotConnect(clients, ips, passes, cons, robotaddress, botnum);
    //}
    //else//go through the process manually
	{
        for(int i = 1; i < ips.size(); i++)
		{
			if(i == botnum)//we do not want to try to connect to ourselves, for that would cause complications
			{
				clients[i];
				cons[i] = false;
				continue;
			}
            clients.insert(pair<int, swarmSocketClient>(i, swarmSocketClient(ips[i].c_str(), 5555)));//insert new client
            cout << "Attempting to initialize socket on client IP " << ips[i] << "...  ";
            b = clients[i].initclient();
			cons[i] = (0 == b);
            cout << (cons[i] ? "Success" : "Failed") << endl;
		}
	}
}

//will wait for host to give the robot their ID and IP list
void Network::waitForHost(string idPrefix, string ipPrefix, string ipSizePrefix)
{
    cout << "1 Getting message\n";
    receiveMessage();
    while(true)
	{        
        if(ms.size() == 0) {
            receiveMessage();
            continue;
        }

        vector<string> msg = ms[0];
        string mess = msg[0];
        ms.erase(ms.begin());
        cout << "1 Message in:  " << mess << endl;
        if(mess.find(idPrefix) != 0)
        {
            cout << "1 Bad prefix:  not " << idPrefix << endl;
            continue;
        }

        mess = mess.substr(idPrefix.size());
        if(atoi(mess.c_str()) < 1)
        {
            cout << "Bad id " << mess << endl;
            continue;
        }
        botnum = atoi(mess.c_str());
        ips[0] = (msg[1].c_str());
        cons[0] = (0 == clients[0].initclient(ips[0].c_str(), 5555));
        cout << "id:  " << botnum << endl;

        cout << "2 Getting message\n";
        while (ms.size() == 0) {
            receiveMessage();
        }
        string sizeMess = ms[0][0];
        ms.erase(ms.begin());
        cout << "2 Message in:  " << sizeMess << endl;
        if (sizeMess.find(ipSizePrefix) != 0) {
            cout << "2 Bad prefix:  not " << ipSizePrefix << endl;
            continue;
        }
        sizeMess = sizeMess.substr(ipSizePrefix.size());

        int ipSize = atoi(sizeMess.c_str());
        if (ipSize < 1) {
            cout << "Bad ips size " << ipSize << endl;
            continue;
        }
        cout << "ips size:  " << ipSize << endl;

        for(int i = 1; i < ipSize; i++) {
            cout << "3 Getting message\n";
            while (ms.size() == 0) {
                receiveMessage();
            }

            string ipMess = ms[0][0];
            ms.erase(ms.begin());
            cout << "3 Message in:  " << ipMess << endl;
            if (ipMess.find(ipPrefix) != 0) {
                cout << "3 Bad prefix:  not " << ipPrefix << endl;
                break;
            }
            ips[i] = ipMess.substr(ipPrefix.size());
            cout << "ip:  " << ips[i] << endl;
        }
        if (ips.size() != ipSize) {
            cout << "Expected " << ipSize << " prefixes, got " << ips.size() << endl;
            continue;
        }

        this->init();
        cout << "init sucessful\n";
        break;
	}
}
void Network::youAreHost(string idPrefix, string ipPrefix, string ipSizePrefix)
{
	botnum = 0;//set ID to 0 since it is the host
	this->init();//initialize
	clients[0];//create a non-connected client to make the size the same
	cons[0] = false;//we are not going to connect to ourself
	ips[0] = "not applicable";
	string mess;
    int ipSize = ips.size();
    for(int i = 1; i < ipSize; i++)
	{
		mess = itoa(i);
		if(cons[i])
		{
            writeMessage(i, idPrefix+mess);//give the robot its ID

            //give the robot its ip list
            writeMessage(i, ipSizePrefix+itoa(ipSize));
            for (int j = 1; j < ipSize; j++) {
                writeMessage(i, ipPrefix+ips[j]);
            }
        }
	}
}
void Network::broadcast(string mess)//sequentially writes the message to each connected robot
{
	for(int i = 0; i < clients.size(); i++)
	{
		if((i != botnum) && cons[i])
		{
			this->writeMessage(i, mess);
		}
	}
}


int Network::writeMessage(int bot, string mess)//writes to a robot based on a ID
{
	int nbytes;
	if(bot < 0 || bot >= clients.size())//if outside of the range return error
	{
		return -4;
	}
	if(!cons[bot])//if not connected return error
	{
		return -5;
	}
	mess = itoa(botnum) + " " + mess;//let the robot know which ID sent it
	nbytes = clients[bot].writeMessage(mess.c_str(), mess.size());//no error so far write to robot
	if(nbytes <= 0)//if error it means we are no longer connected to the robot
	{
		cons[bot] = false;
	}
	return nbytes;//return error
}
int Network::writeMessage(string ip, string mess)//writes to a robot based on a IP
{
	int nbytes, bot;
	bool inMap = false;
	for(map<int, string>::iterator it = ips.begin(); it != ips.end(); it++)
	{
		if(it->second == ip)
		{
			inMap = true;
			bot = it->first;
			break;
		}
	}
	if(!inMap)
	{
		return -4;
	}
	if(!cons[bot])
	{
		return -5;
	}
	mess = itoa(botnum) + " " + mess;
	nbytes = clients[bot].writeMessage(mess.c_str(), mess.size());
	if(nbytes <= 0)
	{
		cons[bot] = false;
	}
	return nbytes;
}
vector<vector<string> > Network::receiveMessage(int sec, int usec)//receives a message from any robot and puts the message into the message buffer
{
	ms = server.read(sec, usec);//sec and usec is the time out, a negative value will make it blocking
	string mess;
	int ID = -1;
	int num;
	string message;
	for(int i = 0; i < ms.size(); i++)
	{
        int firstSpace = ms[i][0].find(' ');
        
		if(firstSpace != string::npos && sscanf(ms[i][0].c_str(), "%i",&ID) == 1)//remove ID from message and correct IP, this is needed for port forwarding
		{            
			ms[i][0] = ms[i][0].substr(firstSpace+1);
			if(getIP(ID) != "" && getIP(ID) != ms[i][1])
			{
				ms[i][1] = getIP(ID);
			}
		}
	}
	return ms;
}
bool Network::isConnected(int bot)
{
	if(cons.find(bot) == cons.end())
	{
		return false;
	}
	return cons[bot];
}
bool Network::refresh(string ackStr)
{
	bool update = false;
	int b;
	for (map<int, string>::iterator it=ips.begin(); it!=ips.end(); ++it)
	{
		if(!cons[it->first] && it->first != botnum)
		{
			clients[it->first].shut();
			b = clients[it->first].initclient(it->second.c_str(), 5555);
			cons[it->first] = (0 == b);
			update = (0 == b) || update;
		}
		else if(it->first != botnum)
		{
            b = writeMessage(it->first, ackStr);
            cons[it->first] = b == ackStr.size();
            update = (! cons[it->first]) || update;
		}
	}
	return update;
}
vector<vector<string> > Network::getMS()
{
	return ms;
}

int Network::getID()
{
	return botnum;
}
int Network::size()
{
	return clients.size();
}

string Network::getIP()
{
	if(ips.find(botnum) == ips.end())
	{
		return "";
	}
	else
	{
		return ips[botnum];
	}
}
string Network::getIP(int ID)
{
	if(ips.find(ID) == ips.end())
	{
		return "";
	}
	else
	{
		return ips[ID];
	}	
}
